﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SqlServerCe;
using System.Data;
using System.IO;
using System.Runtime.Serialization;
using TableTop.DataModel.Text;
using TableTop.DataModel.Shelf;

namespace TableTop.DataManagement.IO
{
    public class Storage
        : IDisposable
    {
        public String StoragePath
        {
            get;
            private set;
        }

        private Object LockObject = new object();
        private SqlCeConnection Connection = null;
        private String strConnectionString = String.Empty;

        public Storage()
            : this("Data")
        {
        }

        public Storage(String StoragePath)
        {
            this.strConnectionString = "Data Source=Database\\Storage.sdf";
            this.Connection = new SqlCeConnection(this.strConnectionString);
            this.Connection.Open();
            this.StoragePath = StoragePath;
            this.CheckDirectory();
        }

        public void Store(Object Data)
        {
            DataContractSerializer dcsSerializer = null;
            FileInfo fiInfo = null;
            FileStream fsStream = null;
            String strFileName = String.Empty;

            if (typeof(ICharacterSheetData).IsInstanceOfType(Data) == true)
            {
                ICharacterSheetData CharacterSheet = (ICharacterSheetData)Data;
                strFileName = String.Format("{0}\\{1}_{2}.sheet",
                                            this.StoragePath,
                                            CharacterSheet.Name,
                                            CharacterSheet.AddonID);          
      
                
                fiInfo = new FileInfo(strFileName);

                // If file already exists, simply overwrite it.

                if (fiInfo.Exists == true)
                    System.Diagnostics.Debug.WriteLine("File already exists, will overwrite");

                dcsSerializer = new DataContractSerializer(Data.GetType());

                try
                {
                    fsStream = fiInfo.Open(FileMode.Create, FileAccess.Write);
                    dcsSerializer.WriteObject(fsStream, Data);
                    fsStream.Flush();
                    fsStream.Close();
                    fsStream.Dispose();
                }
                catch (Exception exc)
                {
                    if (System.Diagnostics.Debugger.IsAttached == true)
                        throw exc;
                    else
                        System.Diagnostics.Debug.WriteLine(exc.ToString());
                }

                if (this.CheckIfExists(CharacterSheet.AddonID,
                                       CharacterSheet.Name,
                                       String.Empty,
                                       strFileName,
                                       TableTopTypes.CharacterSheet) == false)
                {
                    this.RegisterObject(CharacterSheet.AddonID,
                                        CharacterSheet.Name,
                                        String.Empty,
                                        strFileName,
                                        TableTopTypes.CharacterSheet);
                }
                else
                {
                    // To do update stuff here
                }
                return;
            }
        }

        public Object Read(ObjectInfo Info)
        {
            throw new NotImplementedException();
        }

        public ObjectInfo[] GetObjects()
        {
            return GetObjects(Guid.Empty, TableTopTypes.Unknown);
        }

        public ObjectInfo[] GetObjects(Guid AddonID)
        {
            return GetObjects(AddonID, TableTopTypes.Unknown);
        }

        public ObjectInfo[] GetObjects(Guid AddonID, TableTopTypes RequestedType)
        {
            StringBuilder sbQuery = null;
            List<SqlCeParameter> Params = null;
            SqlCeParameter CurrentParameter = null;
            SqlCeCommand Command = null;
            SqlCeDataAdapter DataAdapter = null;
            DataTable DataResult = null;
            ObjectInfoCollection lstObjectInfo = null;

            if (this.Connection.State != System.Data.ConnectionState.Open)
                throw new InvalidOperationException();

            sbQuery = new StringBuilder("SELECT * FROM tblObjects");

            if (AddonID != Guid.Empty || RequestedType != TableTopTypes.Unknown)
            {
                Params = new List<SqlCeParameter>();

                sbQuery.Append(Environment.NewLine);
                sbQuery.Append("WHERE");
                sbQuery.Append(Environment.NewLine);

                if (AddonID != Guid.Empty)
                {
                    sbQuery.Append("AddonID = @AddonID");
                    CurrentParameter = new SqlCeParameter("@AddonID", AddonID);
                    Params.Add(CurrentParameter);
                }


                if (AddonID != Guid.Empty && RequestedType != TableTopTypes.Unknown)
                {
                    sbQuery.Append(Environment.NewLine);
                    sbQuery.Append("AND");
                    sbQuery.Append(Environment.NewLine);
                }

                if (RequestedType != TableTopTypes.Unknown)
                {
                    sbQuery.Append("Type = @RequestedType");
                    CurrentParameter = new SqlCeParameter("@RequestedType", RequestedType);
                    Params.Add(CurrentParameter);
                }
            }

            Command = new SqlCeCommand(sbQuery.ToString(), this.Connection);

            if (Params != null)
                Command.Parameters.AddRange(Params.ToArray());

            DataAdapter = new SqlCeDataAdapter(Command);            
            DataResult = new DataTable();
            DataAdapter.Fill(DataResult);
            DataAdapter.Dispose();
            Command.Dispose();

            lstObjectInfo = new ObjectInfoCollection();

            if (DataResult.Rows.Count > 0)
            {
                for (int i = 0; i < DataResult.Rows.Count; i++)
                {
                    ObjectInfo oiCurrent = null;                    
                    Guid gdAddonID = Guid.Empty;
                    String strName = String.Empty;
                    int iID = -1;
                    int iType = -1;

                    iID = (int)DataResult.Rows[i]["ID"];
                    gdAddonID = (Guid) DataResult.Rows[i]["AddonID"];
                    strName = (String)DataResult.Rows[i]["Name"];
                    iType = (int)DataResult.Rows[i]["Type"];
                    oiCurrent = new ObjectInfo(iID, gdAddonID, strName, (TableTopTypes)iType);
                    lstObjectInfo.Add(oiCurrent);
                }
            }

            return lstObjectInfo.ToArray();
        }

        private void CheckDirectory()
        {
            if (Directory.Exists(this.StoragePath) == false)            
                Directory.CreateDirectory(this.StoragePath);            
        }

        /// <summary>
        /// Check if a specified object already exists over database.
        /// </summary>
        /// <param name="AddonID"></param>
        /// <param name="Name"></param>
        /// <param name="Description"></param>
        /// <param name="Filename"></param>
        /// <param name="ObjectType"></param>
        /// <returns>True if the record exists, false if no record exists</returns>
        /// <remarks>Maybe can be optimized</remarks>
        private bool CheckIfExists(Guid AddonID, String Name, String Description,
                                   String Filename, TableTopTypes ObjectType)
        {
            String strQuery = String.Empty;
            List<SqlCeParameter> Params = null;
            SqlCeCommand Command = null;
            SqlCeDataAdapter DataAdapter = null;
            DataTable dtRows = null;
            bool bResult = false;

            strQuery = "SELECT * FROM tblObjects WHERE " +
                       "AddonID = @AddonID AND Name = @Name AND Description = @Description AND Filename = @Filename " + 
                       "AND Type =  @Type";
            Params = new List<SqlCeParameter>();
            Command = new SqlCeCommand(strQuery, this.Connection);
            Params.Add(new SqlCeParameter("@AddonID", AddonID));
            Params.Add(new SqlCeParameter("@Name", Name));
            Params.Add(new SqlCeParameter("@Description", Description));
            Params.Add(new SqlCeParameter("@Filename", Filename));
            Params.Add(new SqlCeParameter("@Type", ObjectType));
            Command.Parameters.AddRange(Params.ToArray());
            DataAdapter = new SqlCeDataAdapter(Command);
            dtRows = new DataTable();
            DataAdapter.Fill(dtRows);
            if (dtRows.Rows.Count > 0)
                bResult = true;
            else
                bResult = false;
            dtRows.Dispose();
            DataAdapter.Dispose();
            Command.Dispose();

            return bResult;
        }

        private void RegisterObject(Guid AddonID, String Name, String Description,
                                    String Filename, TableTopTypes ObjectType)
        {
            String strQuery = String.Empty;
            List<SqlCeParameter> Params = null;
            SqlCeCommand Command = null;
            int iResult = -1;

            strQuery = "INSERT INTO tblObjects (AddonID, Name, Description, Filename, Type) " +
                       " VALUES(@AddonID, @Name, @Description, @Filename, @Type)";

            Params = new List<SqlCeParameter>();
            Command = new SqlCeCommand(strQuery, this.Connection);
            Params.Add(new SqlCeParameter("@AddonID", AddonID));
            Params.Add(new SqlCeParameter("@Name", Name));
            Params.Add(new SqlCeParameter("@Description", Description));
            Params.Add(new SqlCeParameter("@Filename", Filename));
            Params.Add(new SqlCeParameter("@Type", ObjectType));
            Command.Parameters.AddRange(Params.ToArray());
            iResult = Command.ExecuteNonQuery();
     
        }

        #region IDisposable Members

        public void Dispose()
        {
            if (this.Connection != null)
            {
                this.Connection.Close();
                this.Connection.Dispose();
                this.Connection = null;
            }
        }

        #endregion
    }
}
